/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package src;

import java.awt.Color;
import java.io.File;
import java.sql.*;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.entity.StandardEntityCollection;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.general.DatasetUtilities;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

/**
 *
 * @author Dell
 */
public class Computation {

    double R; // reward when a code is grabbed
    double C; // cost per click
    double a; // reward discount factor
    double lam_code; // code arrival rate
    int num_nodes; // number of nodes in tree
    double[] effort; // effort of each node
    int[] tree_id; // node id of all nodes, node id starts from 1
    int[] tree_parent; // parent of node i is node id tree_parent[i], 0 for root
    int[] tree_isleaf; // is the node leaf, 1 if it is
    int[] tree_depth_first; // node id encountered in depth 1st traversal of the tree
    double[] avg_utility; // computed average utility of all players
    double[] avg_utility_children; // average utility due to all children, grand children etc
    double[] avg_utility_self; // average utility due to self effort, without cost
    double[] avg_cost_self_effort; // average cost due to self effort

    // assuming some tree here, this should be read from database
    // the tree is read in an array called tree_id, tree_parent, tree_isleaf, tree_depth_first.
    // tree_id is node id (redundant because tree_id[i] = i or i+1),
    // tree_parent[i] is parent of the node i, tree_isleaf[i] is 1 if node is leaf, 0 otherwise, 
    // tree_depth_first contains the depth first traversal of the tree. 
    Computation() // constructor does nothing
    {
    }

    /*
     * public void set_parameters() { // read these values from database
     * ========== R = 15;//2.2;//15; C = 1; a = 2; lam_code = 1.0 / 5.0;
     * num_nodes = 8; // ==========================================
     *
     * effort = new double[num_nodes]; tree_id = new int[num_nodes]; tree_parent
     * = new int[num_nodes]; tree_isleaf = new int[num_nodes]; tree_depth_first
     * = new int[num_nodes]; avg_utility = new double[num_nodes];
     * avg_utility_children = new double[num_nodes]; avg_utility_self = new
     * double[num_nodes]; avg_cost_self_effort = new double[num_nodes];
     *
     * // read effort and tree (id, parent, depth_first) from database
     * ================= tree_id[0] = 1 - 1; tree_id[1] = 2 - 1; tree_id[2] = 3
     * - 1; tree_id[3] = 4 - 1; tree_id[4] = 5 - 1; tree_id[5] = 6 - 1;
     * tree_id[6] = 7 - 1; tree_id[7] = 8 - 1;
     *
     * tree_parent[0] = 0 - 1; tree_parent[1] = 1 - 1; tree_parent[2] = 1 - 1;
     * tree_parent[3] = 1 - 1; tree_parent[4] = 3 - 1; tree_parent[5] = 3 - 1;
     * tree_parent[6] = 4 - 1; tree_parent[7] = 6 - 1;
     *
     * tree_isleaf[0] = 0; tree_isleaf[1] = 1; tree_isleaf[2] = 0;
     * tree_isleaf[3] = 0; tree_isleaf[4] = 1; tree_isleaf[5] = 0;
     * tree_isleaf[6] = 1; tree_isleaf[7] = 1;
     *
     * tree_depth_first[0] = 2 - 1; tree_depth_first[1] = 5 - 1;
     * tree_depth_first[2] = 8 - 1; tree_depth_first[3] = 6 - 1;
     * tree_depth_first[4] = 3 - 1; tree_depth_first[5] = 7 - 1;
     * tree_depth_first[6] = 4 - 1; tree_depth_first[7] = 1 - 1;
     *
     * effort[0] = 0; effort[1] = .24; effort[2] = 0; effort[3] = .12; effort[4]
     * = .24; effort[5] = .12; effort[6] = .24; effort[7] = .24;
     *
     * //effort[0] = 0.0100; //effort[1] = 0.0306; //effort[2] = 0.0182;
     * //effort[3] = 0.0322; //effort[4] = 0.0390; //effort[5] = 0.0173;
     * //effort[6] = 0.0239; //effort[7] = 0.0281; //
     * ===================================================== }
     */
    // Initializes the config parameters and the tree.
    public void init() {

        try {

            double[] temp = DataAccess.getConfig();
            int[][] tempTree = new int[num_nodes][4];
            R = temp[0];//2.2;//15;
            C = temp[1];
            a = temp[2];
            lam_code = temp[3];
            num_nodes = (int) temp[4];
            tree_id = new int[num_nodes];
            tree_parent = new int[num_nodes];
            tree_isleaf = new int[num_nodes];
            tree_depth_first = new int[num_nodes];
            avg_utility_children = new double[num_nodes];
            avg_utility_self = new double[num_nodes];
            avg_cost_self_effort = new double[num_nodes];
            avg_utility = new double[num_nodes];
            effort = new double[num_nodes];
            tempTree = DataAccess.getTree(num_nodes);
            int count = 0;
            while (count < num_nodes) {
                tree_id[count] = tempTree[count][0];
                tree_parent[count] = tempTree[count][1];
                tree_isleaf[count] = tempTree[count][2];
                tree_depth_first[count] = tempTree[count][3];
                count++;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Reads player efforts from db and stores them in memory
    public void initEfforts(int userID, double effort1, int no_players) {
        double[] tempEffort = new double[no_players];

        try {

            tempEffort = DataAccess.readEfforts(no_players);

            tempEffort[userID] = effort1;
            for (int i = 0; i < no_players; i++) {
                effort[i] = tempEffort[i];
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void readEffort() {
        double[] tempEffort = new double[num_nodes];

        try {

            tempEffort = DataAccess.readEfforts(num_nodes);

            for (int i = 0; i < num_nodes; i++) {
                effort[i] = tempEffort[i];
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void compute_utility(int userID, double effort_p) {
        try {
            int i;
            double sum_lam = 0;
            // Initializes config and efforts
            init();
            initEfforts(userID, effort_p, num_nodes);


            for (i = 0; i < num_nodes; i++) {
                avg_utility_children[i] = 0; // initialization
                avg_utility_self[i] = 0; // initialization
                avg_cost_self_effort[i] = 0; // initialization
                sum_lam = sum_lam + effort[i]; // initialization
            }

            for (int j = 0; j < num_nodes; j++) {

                avg_cost_self_effort[tree_depth_first[j]] = C * effort[tree_depth_first[j]];

                if (sum_lam >= lam_code) {
                    avg_utility_self[tree_depth_first[j]] = (lam_code * R / a) * effort[tree_depth_first[j]] / sum_lam;
                } else // sum_lam < lam_code 
                {
                    avg_utility_self[tree_depth_first[j]] = (R / a) * effort[tree_depth_first[j]];
                }

                if (tree_isleaf[tree_depth_first[j]] == 1) {
                    avg_utility_children[tree_depth_first[j]] = 0; // leaves dont have children
                }

                if (tree_parent[tree_depth_first[j]] != -1) { // not a root node
                    avg_utility_children[tree_parent[tree_depth_first[j]]] = avg_utility_children[tree_parent[tree_depth_first[j]]] + (1 / a) * (avg_utility_self[tree_depth_first[j]] + avg_utility_children[tree_depth_first[j]]);
                }
            }


            for (i = 0; i < num_nodes; i++) {
                avg_utility[i] = avg_utility_self[i] + avg_utility_children[i] - avg_cost_self_effort[i];
                //    System.out.println("node id " + i + " utility " + avg_utility[i] + " self " + avg_utility_self[i] + " children " + avg_utility_children[i] + " cost " + avg_cost_self_effort[i]);
            }

            // write utilities in database

            //   System.out.println("end of compute_utility in class Computation");
            for (i = 0; i < num_nodes; i++) {
                DataAccess.insertData(i, effort[i], avg_utility[i], avg_utility_children[i], userID);
            }
// To-DO: Create a graph of latest efforts for the admin
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /*
     * Computes the solution
     */
    public double[][] recommend_strategy(int p) {
        double init_pt = 0; // huristically chosen
        double final_pt = lam_code * R / (a * C);  // huristically chosen
        int len = 100;
        double steps = (final_pt - init_pt) / len;  // huristically chosen
        double[] rec_eff_utility = new double[len];
        double[] eff = new double[len];

        double[] avg_utility_children_t = new double[num_nodes];
        double[] avg_utility_self_t = new double[num_nodes];
        double[] avg_cost_self_effort_t = new double[num_nodes];
        double[] effort_t = new double[num_nodes];
        double sum_lam;
        double[][] recommended = new double[len][2];
        try {

            for (int i = 0; i < len; i++) {
                eff[i] = init_pt + i * steps;
                sum_lam = eff[i];
                effort_t[p] = eff[i];

                for (int j = 0; j < num_nodes; j++) {
                    avg_utility_children_t[j] = 0; // initialization
                    avg_utility_self_t[j] = 0; // initialization
                    avg_cost_self_effort_t[j] = 0; // initialization
                    if (j != p) {
                        effort_t[j] = effort[j];
                        sum_lam = sum_lam + effort_t[j];
                    }
                }

                for (int j = 0; j < num_nodes; j++) {

                    avg_cost_self_effort_t[tree_depth_first[j]] = C * effort_t[tree_depth_first[j]];

                    if (sum_lam >= lam_code) {
                        avg_utility_self_t[tree_depth_first[j]] = (lam_code * R / a) * effort_t[tree_depth_first[j]] / sum_lam;
                    } else // sum_lam < lam_code 
                    {
                        avg_utility_self_t[tree_depth_first[j]] = (R / a) * effort_t[tree_depth_first[j]];
                    }

                    if (tree_isleaf[tree_depth_first[j]] == 1) {
                        avg_utility_children_t[tree_depth_first[j]] = 0; // leaves dont have children
                    }

                    if (tree_parent[tree_depth_first[j]] != -1) { // not a root node
                        avg_utility_children_t[tree_parent[tree_depth_first[j]]] = avg_utility_children_t[tree_parent[tree_depth_first[j]]] + (1 / a) * (avg_utility_self_t[tree_depth_first[j]] + avg_utility_children_t[tree_depth_first[j]]);
                    }
                }


                rec_eff_utility[i] = avg_utility_self_t[p] + avg_utility_children_t[p] - avg_cost_self_effort_t[p];
                //   System.out.println("node id " + p + " rec eff " + eff[i] + " utility " + rec_eff_utility[i] + " self " + avg_utility_self_t[p] + " children " + avg_utility_children_t[p] + " cost " + avg_cost_self_effort_t[p]);

            }

            for (int i = 0; i < len; i++) {
                recommended[i][1] = eff[i];    // x-Axis
                recommended[i][0] = rec_eff_utility[i];   //Y-Axis
            }
            // write eff rec_eff_utility in database
        } catch (Exception e) {
            e.printStackTrace();
        }
        return recommended;
    }

    // plots graph of utility vs effort
    /*
     * Generates the solution
     */
    public void graphRecomendation(int userID) {

        XYSeries series = new XYSeries("Utility");
        double[][] data = null;

        try {

            data = recommend_strategy(userID);

            // Date date = new Date();
            //double base_days = ((date.getYear() - 71) * 365 + 9 + 1 + date.getMonth() * 30 + date.getDay()); //Days
            //double base_ms = base_days * 24 * 60 * 60 * 1000;

            for (int i = 0; i < data.length; i++) {

                series.add(data[i][1], data[i][0]);
            }
            String graphTitle = "Recommended Effort Utility plot for Player " + userID;
            XYDataset xyDataset = new XYSeriesCollection(series);
            JFreeChart chart = ChartFactory.createXYLineChart(
                    graphTitle, "Effort", "Utility",
                    xyDataset, PlotOrientation.VERTICAL, true, true, false);
            chart.setBackgroundPaint(new Color(255, 255, 255));

            ChartRenderingInfo info = new ChartRenderingInfo(new StandardEntityCollection());
            String filePath = "H:/code/Outsourced/web/recgraph" + Integer.toString(userID) + ".png";
            File imgFile = new File(filePath);
            ChartUtilities.saveChartAsPNG(imgFile, chart, 500, 400, info);
        } catch (Exception e) {
            System.out.println(e);
        }

    }

    /*
     * Create effort profile barchart for admin
     */
    public void createEffortChart() {

        double[][] data = new double[1][num_nodes];
        double[] temp;
        try {

            temp = DataAccess.readEfforts(num_nodes);
            System.arraycopy(temp, 0, data[0], 0, num_nodes);

            CategoryDataset dataset = DatasetUtilities.createCategoryDataset("Effort",
                    "", data);
            JFreeChart chart = ChartFactory.createBarChart(
                    "Effort Profile", "", "Effort",
                    dataset, PlotOrientation.VERTICAL, true, true, false);

            ChartRenderingInfo info = new ChartRenderingInfo(new StandardEntityCollection());
            String filePath = "H:/code/Outsourced/web/Check_converge.png";
            File imgFile = new File(filePath);
            ChartUtilities.saveChartAsPNG(imgFile, chart, 600, 400, info);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
